Skip to content

[eas-cli] Write the reason a command failed to stderr - #4592

Merged
vonovak merged 3 commits into
vonovak/dependency-notice-ttyfrom
vonovak/failure-reason-stderr
Oct 27, 2026
Merged

vonovak merged 3 commits into
vonovak/dependency-notice-ttyfrom
vonovak/failure-reason-stderr

Conversation

@vonovak

@vonovak vonovak commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Why

When a command fails, EasCommand.catch prints the (helpful) reason with Log.error, which writes to stdout. Then oclif prints <command> command failed. to stderr. A program that reads stderr for the error finds only the generic (not helpful) line.

Stacked on #4591. This is the second of four PRs: #4591 ← #4592 (this PR) ← #4593 ← #4594.

How

Add Log.errorToStderr and use it in EasCommand.catch only. Other Log.error and Log.warn calls stay on stdout:

  • Some Log.error calls are not failures and exit 0, for example in env:get and in the Android keystore fallback to cloud generation.
  • ora.ts keeps non-TTY output on stdout on purpose, because some CI systems treat stderr as a failure. A failing command already writes to stderr through oclif, so this PR adds no new stderr output to a run that succeeds.

With --json, nothing changes: stdout was already redirected to stderr.

Trade-off: a program that reads stderr for errors now finds the reason there, and a program that parses stdout no longer gets the reason mixed into it. A program that reads the reason from stdout must read stderr instead, so the changelog lists this under breaking changes. The callers in this repo that run eas-cli (build-tools and eas-build-cache-provider) read stdout only on success. On failure they log stderr or merge both streams.

LLM Authorship

  • Vojtech asked for errors on stderr. Claude Code wrote the change.

Test Plan

  • Real run, not logged in, build:list --non-interactive: the reason (Either log in with eas login ...) is on stderr, next to Error: build:list command failed.. On 24.12.1 it was on stdout.
  • tsc, oxlint and oxfmt are clean.

🤖 Generated with Claude Code

@codecov

codecov Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.58%. Comparing base (e2598e5) to head (0a85cda).

Files with missing lines Patch % Lines
packages/eas-cli/src/log.ts 0.00% 3 Missing ⚠️
Additional details and impacted files
@@                        Coverage Diff                        @@
##           vonovak/dependency-notice-tty    #4592      +/-   ##
=================================================================
- Coverage                          75.59%   75.58%   -0.00%     
=================================================================
  Files                               1054     1054              
  Lines                              52437    52440       +3     
  Branches                           11459    11459              
=================================================================
  Hits                               39634    39634              
- Misses                             11822    11825       +3     
  Partials                             981      981              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@vonovak
vonovak added this pull request to stack #4595 October 9, 2026 19:52
@vonovak
vonovak force-pushed the vonovak/failure-reason-stderr branch from 0559704 to 3cb54f7 Compare October 12, 2026 08:04
@vonovak
vonovak marked this pull request as ready for review October 12, 2026 08:08
@github-actions

Copy link
Copy Markdown

Subscribed to pull request

File Patterns Mentions
packages/eas-cli/** @douglowder

Generated by CodeMention

Warning: The preamble and epilogue options in commentConfiguration are deprecated. Use template instead.

@vonovak
vonovak requested a review from douglowder October 12, 2026 08:08
@douglowder

Copy link
Copy Markdown
Contributor

eas-cli now uses changesets for changelogs (#4580). The required "Check for a changeset" check fails on PRs that edit CHANGELOG.md.

To update this PR:

  1. Revert the changes to CHANGELOG.md.
  2. Run yarn changeset, select the changed packages and the bump type, and write the summary.
  3. Commit the new file in .changeset/.

The release adds the PR link and the author to the entry. If this PR needs no changelog entry, revert the CHANGELOG.md changes and add the "no changelog" label.

@vonovak
vonovak force-pushed the vonovak/failure-reason-stderr branch from 3cb54f7 to e9ab32e Compare October 15, 2026 14:41

@douglowder douglowder left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change looks good to me: it moves only the failure reason in EasCommand.catch, so a successful run writes nothing new to stderr. A few small notes:

  • Description: The "How" section says Log.error in env:delete exits 0, but env:delete throws right after it (Variable "…" not deleted.). env:get is a correct example; the keystore fallback in credentials/android/utils/keystore.ts (Failed to generate keystore locally. Falling back to cloud generation.) is another.
  • Color in redirected stderr: errorToStderr uses the default chalk instance, which takes its color level from stdout, so eas … 2>err.log in a terminal writes color codes to the file. A chalk instance with its level from stderr would fix it. Fine to leave as the noted known limit if you prefer to keep this PR small.
  • Tests: The updated tests check that Log.errorToStderr is called, not that the output reaches stderr. Acceptable for a one-line function, since the live build:list run in the test plan covers the stream.

vonovak and others added 2 commits October 26, 2026 20:01
EasCommand.catch printed the reason with Log.error, which writes to
stdout. oclif then printed "<command> command failed." to stderr. A
program that reads stderr for the error found only that generic line.

Only the catch changes. Other Log.error and Log.warn calls stay on
stdout, because some of them are not failures and the spinner keeps
non-TTY output off stderr on purpose.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vonovak
vonovak force-pushed the vonovak/failure-reason-stderr branch from e3c1933 to 94fe3c4 Compare October 26, 2026 19:47
chalk's default instance takes its color level from stdout, so
`eas ... 2>err.log` in a terminal wrote color codes to the file.

Also spy on errorToStderr in the already-printed catch test. The catch
no longer calls Log.error, so the test passed even without the
messageAlreadyPrinted guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vonovak
vonovak force-pushed the vonovak/failure-reason-stderr branch from 4a9e606 to 0a85cda Compare October 26, 2026 20:40
@vonovak

vonovak commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

The change looks good to me: it moves only the failure reason in EasCommand.catch, so a successful run writes nothing new to stderr. A few small notes:

  • Description: The "How" section says Log.error in env:delete exits 0, but env:delete throws right after it (Variable "…" not deleted.). env:get is a correct example; the keystore fallback in credentials/android/utils/keystore.ts (Failed to generate keystore locally. Falling back to cloud generation.) is another.
  • Color in redirected stderr: errorToStderr uses the default chalk instance, which takes its color level from stdout, so eas … 2>err.log in a terminal writes color codes to the file. A chalk instance with its level from stderr would fix it. Fine to leave as the noted known limit if you prefer to keep this PR small.
  • Tests: The updated tests check that Log.errorToStderr is called, not that the output reaches stderr. Acceptable for a one-line function, since the live build:list run in the test plan covers the stream.

@douglowder I updated the description, and fixed the color in redirected stderr: 0a85cda

@github-actions

Copy link
Copy Markdown

✅ Thank you for adding a changeset!

@douglowder
douglowder self-requested a review October 26, 2026 22:56
@vonovak
vonovak merged commit 8070663 into main Oct 27, 2026
12 of 13 checks passed
@vonovak
vonovak deleted the vonovak/failure-reason-stderr branch October 27, 2026 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants